<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>JavaScript一些现象(Some phenomena in JavaScript)</title>
  <!-- 
  JavaScript 是一种动态类型的编程语言，具有一些独特的现象和特性。
  1. 变量提升（Hoisting）
  - 概念：在 JavaScript 中，变量的声明（但不包括赋值）会被提升到函数或全局作用域的顶部。
    console.log(x); // 输出: undefined
    var x = 5;

  - 总结：虽然变量  `x`  在声明之前被访问，结果仍然是  `undefined` ，因为变量的声明被提升了。

  2. 作用域（Scope）
  - 概念：作用域是变量的可访问区域，JavaScript 主要有全局作用域和函数作用域。
    var globalVar = 'I am global';
    function testScope() {
      var localVar = 'I am local';
      console.log(globalVar); // 可以访问
    }

    testScope();
    console.log(localVar); // 报错: localVar is not defined

  - 总结：局部变量只能在其定义的函数内部访问，而全局变量可以在任何地方访问。

  3. 闭包（Closure）
  - 概念：闭包是指一个函数可以访问其外部作用域的变量，即使外部函数已经返回。
    function outerFunction() {
      var outerVar = 'I am outside!';
      return function innerFunction() {
        console.log(outerVar);
      };
    }

    var closureFunc = outerFunction();
    closureFunc(); // 输出: I am outside!

  - 总结：闭包允许函数记住其外部环境的状态，可以用于数据封装和私有变量。

  4. 异步编程（Asynchronous Programming）
  - 概念：JavaScript 是单线程的，但可以通过回调函数、Promise 和 async/await 来实现异步操作。
    console.log('Start');
    setTimeout(() => {
        console.log('Timeout');
    }, 1000);

    console.log('End');

  - 总结：异步编程允许 JavaScript 在等待某些操作（如网络请求）时继续执行其他代码，提升应用的响应性。

  5. 原型链（Prototype Chain）
  - 概念：JavaScript 使用原型链实现继承，所有对象都有一个内部链接指向其原型。
    function Person(name) {
      this.name = name;
    }

    Person.prototype.sayHello = function() {
        console.log('Hello, ' + this.name);
    };

    var john = new Person('John');
    john.sayHello(); // 输出: Hello, John

  - 总结：通过原型链，子对象可以访问父对象的属性和方法，实现了继承的效果。

  6. NaN 的特性
  - 概念： `NaN`  表示“不是一个数字”，并且是一个特殊的值。
    console.log(NaN === NaN); // 输出: false
    console.log(isNaN(NaN)); // 输出: true
  - 总结： `NaN`  与任何值（包括自身）都不相等，使用  `isNaN()`  函数检查是否为  `NaN` 。

  总结
  JavaScript 中的这些现象和特性构成了其独特的行为模式。
   -->
</head>
<body>

  <script>

    // 1. 对象全等比较（Object Equality）
    var obj1 = { a: 1 };
    var obj2 = { a: 1 };
    const obj3 = {}
    const obj4 = {}
    const arr1 = []
    const arr2 = []
    console.log(obj1 === obj2, obj1 == obj2); // 输出: false false
    console.log(obj3 === obj4, obj3 == obj4); // 输出: false false
    console.log(arr1 === arr2, arr1 == arr2); // 输出: false false

    // 2. 函数作用域（Function Scope）
    var x = 1;
    function foo() {
      var x = 2;
      console.log(x); // 输出: 2
    }
    foo();

    // 3. 引用赋值
    const info = {
      name: 'John',
      age: 30,
      friend: {
        name: 'Tom',
        age: 25
      }
    }
    
    let friend = info.friend;
    friend.name = 'Jerry';
    console.log(info.friend.name); // 输出: Jerry

    // 4. 值传递
    function changeValue(value) {
      value = 10;
    }
    let num = 5;
    changeValue(num); // 传递的是 num 的值，而不是引用
    console.log(num); // 输出: 5

    // 5. NaN 与任何值都不相等
    console.log(NaN === NaN); // 输出: false
    console.log(NaN == NaN); // 输出: false
    console.log(isNaN(NaN)); // 输出: true

    // 6.1 引用传递 - 对象
    function changeObject(obj) {
      obj.name = 'Tom';
    }
    let person = { name: 'John' };
    changeObject(person);
    console.log("6.1 引用传递 - 对象", person.name); // 输出: Tom

    // 6.2 引用传递 - 数组
    function changeArray(arr) {
      arr.push(4);
    }
    let numbers = [1, 2, 3];
    changeArray(numbers);
    console.log("6.2 引用传递 - 数组", numbers); // 输出: [1, 2, 3, 4]

    // 6.3 引用传递 - 字符串
    // 字符串是不可变的，所以传递字符串时，实际上传递的是字符串的引用
    function changeString(str) {
      str = 'Hello';
    }
    let greeting = 'World';
    changeString(greeting);
    console.log("6.3 引用传递 - 字符串", greeting); // 输出: World

    // 6.4 引用传递 - 函数
    function changeFunction(func) {
      func = function() {
        console.log('6.4 引用传递 - 函数 New function');
      };
      func()
    }
    let myFunc = function() {
      console.log('6.4 引用传递 - 函数 Original function');
    };
    changeFunction(myFunc); // 输出: 6.4 引用传递 - 函数 New function
    myFunc(); // 输出: 6.4 引用传递 - 函数 Original function

    // 6.5 引用传递 - 对象引用
    function objFn(obj) {
      obj = { name: 'Tom' };
    }

    let obj = { name: 'John' };
    objFn(obj);
    console.log("6.5 引用传递 - 对象引用", obj.name); // 输出: John
    
  </script>
  
</body>
</html>